/*
 *
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License version 2 as 
 * published by the Free Software Foundation.
 *
 * memory_pool.cpp is for what ...
 *
 * Version: ***: memory_pool.cpp  Sat Jul 23 09:23:09 2011 poponion Exp $
 *
 * Authors:
 *   Author poponion <huangcm85@gmail.com>
 *     -some work detail if you want 
 *
 */

#include <new>
#include "memory_pool.h"

void* MemoryPool::MemBlock::alloc()
{
  void* ret = NULL;
  if (0 != free_num_)
  {
    ret = reinterpret_cast<void *>(data_ + item_size_ * free_pos_);
    --free_num_;
    free_pos_ = *reinterpret_cast<int32_t *>(ret);
  }
  return ret;
};

void MemoryPool::MemBlock::free(void * ptr)
{
  *reinterpret_cast<int32_t *>(ptr) = free_pos_;
  free_pos_ = (reinterpret_cast<char *>(ptr) - data_) / item_size_;
  ++free_num_;
};

MemoryPool::MemoryPool(int32_t item_size, int32_t item_num):item_size_(item_size),
                                          item_num_(item_num),
                                          block_head_(NULL)
{
}

MemoryPool::~MemoryPool()
{
  destory();
}

void* MemoryPool::mem_alloc()
{
  void* ret = NULL;
  MemBlock* iter = block_head_;
  while(NULL != iter)
  {
    ret = iter->alloc();
    if ( NULL != ret )
    {
      break;
    }
    iter = iter->next();
  }

  //There are no memory can alloc from memory pool
  //malloc memory from system and build a new MemBlock
  if( NULL == ret )
  {
    int32_t alloc_bytes = sizeof(MemBlock) + item_size_ * item_num_;
    void * ptr = malloc(alloc_bytes);
    if (NULL == ptr)
    {
      fprintf(stderr, "fatal error! Can not malloc memory from system. Just exit\n");
      exit(-1);
    }
    MemBlock* new_block = new(ptr)MemBlock(item_num_, item_size_);
    new_block->set_next(block_head_);
    block_head_ = new_block;
    ret = new_block->alloc();
    if (NULL == ret)
    {
      fprintf(stderr, "ERROR Can not alloc memory from a new memblock");
    }
  }
  return ret;
}

void MemoryPool::mem_free(void * ptr)
{
  MemBlock* iter = block_head_;
  while( NULL != iter)
  {
    if( reinterpret_cast<char*>(ptr) >= reinterpret_cast<char *>(iter->data())
        && (reinterpret_cast<char*>(ptr) < (reinterpret_cast<char *>(iter->data()) + item_size_ * item_num_)))
    {
      iter->free(ptr);
      break;
    }
    iter = iter->next();
  }
}

void MemoryPool::destory()
{
  MemBlock* iter = block_head_;
  while( NULL != iter )
  {
    MemBlock* iter_next = iter->next();
    free(iter);
    iter = iter_next;
  }
  block_head_ = NULL;
  item_size_ = 0;
  item_num_ = 0;
}
